/**
 *
 */
package com.zy.pay.api.util;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

import javax.net.ssl.SSLContext;

import org.apache.commons.lang.StringUtils;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * http工具类
 *
 * @author: luochao
 * @since: 2017年1月19日 上午10:05:51
 * @history:
 */
public class HttpUtil {

    private static final Logger                       logger                      = LoggerFactory.getLogger(HttpUtil.class);
    // httpclient
    private static CloseableHttpClient                httpclient                  = null;
    private static CloseableHttpClient                httpsclient                 = null;
    private static RequestConfig                      requestConfig               = null;
    private static PoolingHttpClientConnectionManager connManager                 = null;

    private static ConnectionKeepAliveStrategy        connectionKeepAliveStrategy = (httpResponse, httpContext) -> 5000;

    private static HttpRequestRetryHandler            handler                     = (e, i,
                                                                                     httpContext) -> i <= 3
                                                                                                     && (e instanceof UnknownHostException
                                                                                                         || e instanceof ConnectTimeoutException
                                                                                                         || e instanceof NoHttpResponseException);

    static {
        //参考http://jinnianshilongnian.iteye.com/blog/2089792
        //连接2s,响应15s,从连接池获取连接2s
        requestConfig = RequestConfig.custom().setConnectTimeout(2000).setSocketTimeout(15000).setConnectionRequestTimeout(2000).build();
        //设置连接池大小
        connManager = new PoolingHttpClientConnectionManager();
        connManager.setMaxTotal(1000);
        connManager.setDefaultMaxPerRoute(200);
    }

    // 参考 http://blog.csdn.net/mn960mn/article/details/48520199
    // 参考 http://blog.csdn.net/z69183787/article/details/48373583
    static {
        try {
            // 忽略证书
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();

            httpsclient = HttpClientBuilder.create().setSSLContext(sslContext).setDefaultRequestConfig(requestConfig)
                .setConnectionManager(connManager).setKeepAliveStrategy(connectionKeepAliveStrategy).setRetryHandler(handler).build();

        } catch (Exception e) {
            throw new RuntimeException("忽略证书与会话保持处理异常", e);
        }
    }

    static {
        httpclient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connManager)
            .setKeepAliveStrategy(connectionKeepAliveStrategy).setRetryHandler(handler).build();
    }

    /***
     *
     * sentPost:发送post请求， 注此方法适用于ContentType=application/x-www-form-urlencoded
     *
     * @param url
     * @param jsonObject
     */
    public static String sendPost(String url, JSONObject jsonObject) {
        Map<String, String> map = new HashMap(jsonObject.size());
        Iterator var4 = jsonObject.keySet().iterator();

        while (var4.hasNext()) {
            String key = (String) var4.next();
            String value = jsonObject.getString(key);
            if (value != null) {
                map.put(key, value);
            }
        }

        return sendPost(url, map);
    }

    /***
     *
     * sentPost:发送post请求， 注此方法适用于ContentType=application/x-www-form-urlencoded
     *
     * @author luochao
     * @date 2018年1月29日 上午11:37:04
     * @param url
     * @param map
     * @return
     */
    public static String sendPost(String url, Map<String, String> map) {
        return sendPost(url, map, new HashMap<String, String>(), ContentType.APPLICATION_FORM_URLENCODED);
    }

    /****
     *
     * sendPost:发送post请求
     *
     * @author luochao
     * @date 2018年1月29日 上午11:39:45
     * @param url 地址
     * @param map 参数
     * @param headMap 请求头中的参数
     * @param contentType 请求类型
     * @return
     */
    public static String sendPost(String url, Map<String, String> map, Map<String, String> headMap, ContentType contentType) {
        HttpPost httppost = new HttpPost(url);
        assembleHeader(httppost, headMap, contentType);
        httppost.setEntity(assembleParam(map, contentType));

        CloseableHttpResponse response = null;
        try {
            long start = System.currentTimeMillis();
            if (isHttps(url)) {
                response = httpsclient.execute(httppost);
            } else {
                response = httpclient.execute(httppost);
            }
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                logger.error("聚合支付服务访问异常，url-->{}, param-->{}", url, JSON.toJSONString(map));
                throw new RuntimeException("聚合支付服务访问异常");
            }
            return toString(response.getEntity());
        } catch (ConnectTimeoutException | SocketTimeoutException | UnknownHostException e) {
            logger.error("聚合支付服务访问超时，url-->{}, param-->{}", url, JSON.toJSONString(map), e);
            throw new RuntimeException("聚合支付服务访问超时", e);
        } catch (Exception e) {
            logger.error("聚合支付服务访问失败，url-->{}, param-->{}", url, JSON.toJSONString(map), e);
            throw new RuntimeException("聚合支付服务访问失败", e);
        } finally {
            IOUtils.closeQuietly(response);
        }
    }

    /***
     *
     * assembleParam:组装参数.
     *
     * @author luochao
     * @date 2018年1月27日 下午12:59:54
     * @param map 参数
     * @param contentType 参数类型
     * @return
     */
    private static StringEntity assembleParam(Map<String, String> map, ContentType contentType) {
        StringEntity stringEntity = null;

        if (ContentType.APPLICATION_JSON.equals(contentType)) {
            stringEntity = new StringEntity(JSON.toJSONString(map), "UTF-8");
            stringEntity.setContentType("application/json");
        } else {
            List<NameValuePair> formparams = new ArrayList<NameValuePair>();
            for (Entry<String, String> entry : map.entrySet()) {
                formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            stringEntity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
        }

        return stringEntity;
    }

    /**
     *
     * assembleHeader:组装请求头.
     *
     * @author luochao
     * @date 2018年1月27日 下午1:58:03
     * @param request 请求头
     * @param contentType 请求类型
     */
    private static void assembleHeader(HttpRequestBase request, Map<String, String> headMap, ContentType contentType) {
        if (ContentType.APPLICATION_JSON.equals(contentType)) {
            request.addHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8");
            request.addHeader(HttpHeaders.ACCEPT, "application/json");
        }
        if (headMap != null && headMap.size() > 0) {
            Iterator<Entry<String, String>> iterator = headMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Entry<String, String> next = iterator.next();
                request.addHeader(next.getKey(), next.getValue());
            }
        }
    }

    /***
     *
     * toString:httpEntity转换为字符串.
     *
     * @author luochao
     * @date 2018年1月27日 下午1:34:33
     * @param httpEntity
     * @return
     */
    private static String toString(HttpEntity httpEntity) {
        String result = null;
        try {
            result = EntityUtils.toString(httpEntity, Consts.UTF_8);
        } catch (ParseException | IOException e) {
            throw new RuntimeException("转字符串异常", e);
        }

        return result;
    }

    /**
     *
     * 判断是否是https请求
     *
     * @author luochao
     * @date 2018年2月11日 上午9:15:47
     * @param url
     * @return
     */
    public static boolean isHttps(String url) {
        if (!StringUtils.isEmpty(url) && url.startsWith("https")) {
            return true;
        }
        return false;
    }

}
